#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <libgen.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <poll.h>


static inline void print_usage(char *progname);
int   socket_server_init(char *listen_ip, int listen_port);

int main(int  argc,char    *argv[])
{

	int                      daemon_run=0;
	int                      rv=-1;
	int                      i=0;
	char                     buf[1024];
	/**********socket需要的变量******************/
	int                      server_listen_port=0;
	char                    *server_listen_ipaddr=NULL;
	int                      lis_fd=-1,cli_fd=-1;

	/********************************************/
	/***********poll需要的变量******************/
	struct  pollfd           poll_fds_array[1024];
	nfds_t                   maxfd=0;
	int                      found=0;

	/****************************getopt_lpng()模块********************************/
	struct  option longopts[]={
		{"port",required_argument,NULL,'p'},
		{"ip",required_argument,NULL,'i'},
		{"help",no_argument,NULL,'h'},
		{"daemon",no_argument,NULL,'d'},
		{NULL,0,NULL,0}//为什么要加？
	};
	while( (rv=getopt_long(argc,argv,"i:dp:h",longopts,NULL)) != -1 )
	{
		switch(rv)
		{
			case 'i':
				server_listen_ipaddr=optarg;
				break;
			case 'p':
				server_listen_port=atoi(optarg);
				break;
			case 'h':
				print_usage(basename(argv[0]));
				return 0;
			case 'd':
				daemon_run=1;
				break;
			default:
				break;
		}
	}
	/*********************************************************************/

	/******未指定监听端口则报错模块**********/
	if(!server_listen_port)
	{

		print_usage(basename(argv[0]));
		return -1;
	}
	/****************************************/

	/******sokcet、listen、bind组合模块，函数返回监听的文件描述符*******************/
	lis_fd=socket_server_init(server_listen_ipaddr, server_listen_port);
	if(lis_fd < 0)
	{
		printf("socket init failure\n");
		return -1;
	}
	/********************************************************************************/

	/***daemon启动模块*****/
	if(daemon_run==1)
	{
		daemon(0,0);
	}
	/*********************/
	/********************************************************************************************/

	for(i=0;i<1024;i++)
	{
		poll_fds_array[i].fd=-1;
	}
	poll_fds_array[0].fd=lis_fd;
	poll_fds_array[0].events=POLLIN;

	printf("poll_fds_array[0].fd=%d\n",poll_fds_array[0].fd);
	while(1)
	{
		printf("\npoll waiting things...\n");
		rv=poll(poll_fds_array,maxfd+1,-1);
		if(rv<0)
		{
			printf("select failure: %s\n", strerror(errno));
			break;
		}
		else if(rv==0)
		{
			printf("select get timeout\n");
			continue;
		}
		printf("poll_rv=%d\n",rv);
		printf("poll accept new thing\n");
		/****poll到新客户端****************/
		if(poll_fds_array[0].revents & POLLIN)
		{
			printf("the thing is accept a new client\n");
			cli_fd=accept(poll_fds_array[0].fd,(struct sockaddr *)NULL,NULL);
			printf("cli_fd=%d\n",cli_fd);
			if(cli_fd<0)
			{
				printf("accept new client failure: %s\n", strerror(errno));
				break;
			}
			printf("accept new client[%d] successfully,now add it's fd into fds_array,and set it's events,and update maxfd\n",cli_fd);
			found=0;
			for(i=1;i<1024;i++)
			{
				if( (poll_fds_array[i].fd) != -1)
					continue;
				poll_fds_array[i].fd=cli_fd;
				poll_fds_array[i].events=POLLIN;
				maxfd= i > maxfd ? i : maxfd;
				printf("maxfd=%ld\n",maxfd);
				found=1;
				break;
			}

			if(found == 0)
			{
				printf("poll_fds_array is full,so refuse client[%d] connect\n",cli_fd);
				close(cli_fd);
				continue;
			}

			/*if (--rv <= 0)
			{
				printf("after -- poll_rv=%d\n",rv);
			       	continue;
			}*/
		}
		/*******************************************************************************/

		/***********************接受到数据********************************************/
		else
		{
			printf("the thing is accept data\n");
			memset(buf,0,sizeof(buf));
			for(i=1;i<1024;i++)
			{
				if( (poll_fds_array[i].fd < 0) || !(poll_fds_array[i].revents & POLLIN))
					continue;
				rv=read(poll_fds_array[i].fd,buf,sizeof(buf));
				if(rv <= 0)
				{
					printf("socket[%d] read failure or get disconncet.\n", poll_fds_array[i].fd);
				       	close(poll_fds_array[i].fd);
				       	poll_fds_array[i].fd = -1;
					break;
				}
				printf("read get %d bytes data from client[%d]:%s\n",rv,poll_fds_array[i].fd,buf);

				rv=write(poll_fds_array[i].fd,buf,strlen(buf));
				if(rv<0)
				{
					printf(" write back to client[%d] failure: %s\n", poll_fds_array[i].fd, strerror(errno));
				       	close(poll_fds_array[i].fd);
				       	poll_fds_array[i].fd = -1;
					break;
				}
				printf("write back to client[%d] successfully:%s\n",poll_fds_array[i].fd,buf);
				break;
			}
		}

	}

 
	/*******************************************************************************************/
	for(i=0;i<1024;i++)
	{
		if(poll_fds_array[i].fd = -1)
			continue;
 
		close(poll_fds_array[i].fd);
	}
	return 0;
}

static inline void print_usage(char *progname)
{
        printf("Usage: %s [OPTION]...\n", progname);
        printf(" %s is a socket server program, which used to verify client and echo back string from it\n",
progname);
        printf("\nMandatory arguments to long options are mandatory for short options too:\n");
        printf(" -b[daemon ] set program running on background\n");
        printf(" -p[port ] Socket server port address\n");
        printf(" -h[help ] Display this help information\n");
        printf("\nExample: %s -b -p 8900\n", progname);
        return ;
}


int socket_server_init(char *listen_ip, int listen_port)
{
        struct sockaddr_in servaddr;
        int rv = 0;
        int on = 1;
        int listenfd;
 if ( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
 {
         printf("Use socket() to create a TCP socket failure: %s\n", strerror(errno));
         return -1;
 }
 /* Set socket port reuseable, fix 'Address already in use' bug when socket server restart */

 setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
 memset(&servaddr, 0, sizeof(servaddr));
 servaddr.sin_family = AF_INET;
 servaddr.sin_port = htons(listen_port);
 if( !listen_ip ) /* Listen all the local IP address */
 {

         servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 }
 else /* listen the specified IP address */
 {
         if (inet_pton(AF_INET, listen_ip, &servaddr.sin_addr) <= 0)
         {
                 printf("inet_pton() set listen IP address failure.\n");
                 rv = -2;//函数返回值
                 goto CleanUp;
         }
 }
 if(bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
 {
         printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
         rv = -3;
         goto CleanUp;
 }
 if(listen(listenfd, 13) < 0)
 {
         printf("Use bind() to bind the TCP socket failure: %s\n", strerror(errno));
         rv = -4;
         goto CleanUp;
 }
CleanUp:
 if(rv<0)
         close(listenfd);
 else
         rv = listenfd;
 return rv;
}

